﻿//#define ZDEBUG

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace ZombieGame
{
    public abstract class Drawable : IDisposable, ICloneable
    {
        public Vector2 Position;
        public Vector2 Scale;
        public Vector2 Size;
        public Vector2 Origin;
        public Vector2 Velocity;
        public Color Color;
        public List<Texture2D> Animation;
        public List<Color[,]> ImageData;
        
        public Matrix Matrix;
        public float Rotation;
        public int Frame;
        public int State;
        public int CurrentFrame { get { return 0; } }
        public bool Visible = true;
        public bool Disposed;
        public abstract float Layer { get; }

        public Drawable()
        {
            Animation = new List<Texture2D>();
            ImageData = new List<Color[,]>();
            Scale = new Vector2(1f);
            UpdateMatrix();
        }

        public virtual void Dispose()
        {
            Position = Vector2.Zero;
            Scale = Vector2.Zero;
            Size = Vector2.Zero;
            Origin = Vector2.Zero;
            Velocity = Vector2.Zero;
            Rotation = 0f;
            Color = Color.Transparent;
            Animation = null;
            Frame = 0;
            State = 0;
            Visible = false;
            Disposed = true;
        }

        public virtual void Draw(SpriteBatch batch)
        {
#if ZDEBUG
            for (int y = Bounds.Top; y <= Bounds.Bottom; y++)
            {
                batch.Draw(ZombieGame.damage, new Vector2(Bounds.Left, y), Color);
                batch.Draw(ZombieGame.damage, new Vector2(Bounds.Right, y), Color);
            }
            for (int x = Bounds.Left + 1; x < Bounds.Right; x++)
            {
                batch.Draw(ZombieGame.damage, new Vector2(x, Bounds.Top), Color);
                batch.Draw(ZombieGame.damage, new Vector2(x, Bounds.Bottom), Color);
            }
#endif
            if (Visible && !Disposed && Animation[State + Frame] != null)
            {
                batch.Draw(Animation[State + Frame], Position, null, Color, Rotation, Origin, Scale, SpriteEffects.None, Layer);
            }
        }

        public virtual void Update(GameTime time)
        {
            Position += Velocity * (float)time.ElapsedGameTime.TotalSeconds;
            UpdateMatrix();
        }

        public void UpdateMatrix()
        {
            Matrix = Matrix.CreateTranslation(new Vector3(-Origin, 0f)) * Matrix.CreateRotationZ(Rotation) * Matrix.CreateScale(new Vector3(Scale, 1f)) * Matrix.CreateTranslation(new Vector3(Position, 0f));
        }

        public static bool CheckCollisionR(Drawable e1, Drawable e2, out Rectangle rect)
        {
            Rectangle col = Rectangle.Intersect(e1.Bounds, e2.Bounds);
            rect = col;
            return col.Width != 0 && col.Height != 0;
        }

        public static bool CheckCollisionB(Drawable e1, Drawable e2)
        {
            Rectangle col;
            if (CheckCollisionR(e1, e2, out col))
            {
                for (int x = 0; x < col.Width; x++)
                    for (int y = 0; y < col.Height; y++)
                    {
                        Vector2 pos1 = e1.ScreenToClient(new Vector2(x, y) + new Vector2(col.X, col.Y));
                        Vector2 pos2 = e2.ScreenToClient(new Vector2(x, y) + new Vector2(col.X, col.Y));
                        if (pos1.X > -1 && pos1.Y > -1 && pos1.X < e1.Size.X && pos1.Y < e1.Size.Y && pos2.X > -1 && pos2.Y > -1 && pos2.X < e2.Size.X && pos2.Y < e2.Size.Y && e1.ImageData[e1.CurrentFrame][(int)pos1.X, (int)pos1.Y].A > 0 && e2.ImageData[e2.CurrentFrame][(int)pos2.X, (int)pos2.Y].A > 0)
                            return true;
                    }
            }
            return false;
        }

        public static bool DoCollisionVelocity(Drawable e1, Drawable e2)
        {
            bool ret = CheckCollisionB(e1, e2);
            if (ret)
            {
                Vector2 va = e1.Velocity, vb = e2.Velocity;
                e1.Velocity = (vb + va) / 1.9f;
                e2.Velocity = (va + vb) / 1.9f;
            }
            return ret;
        }

        public static Color[,] TextureToColor(Texture2D texture)
        {
            Color[] colors1D = new Color[texture.Width * texture.Height];
            texture.GetData(colors1D);
            Color[,] colors2D = new Color[texture.Width, texture.Height];
            for (int x = 0; x < texture.Width; x++)
                for (int y = 0; y < texture.Height; y++)
                    colors2D[x, y] = colors1D[x + y * texture.Width];
            return colors2D;
        }

        public Rectangle Bounds
        {
            get
            {
                if (this is Player)
                {
                    MathHelper.ToRadians(0f);
                }
                Vector2[] c1 = new Vector2[4] { new Vector2(0f, 0f), new Vector2(Size.X, 0f), Size, new Vector2(0f, Size.Y) };
                Vector2[] v1 = new Vector2[4];
                Vector2.Transform(c1, ref Matrix, v1);
                Vector2? tl = null, br = null;
                foreach (Vector2 v in v1)
                {
                    if (tl == null)
                        tl = v;
                    if (br == null)
                        br = v;
                    if (v.X <= tl.Value.X)
                        tl = new Vector2(v.X, tl.Value.Y);
                    if (v.Y <= tl.Value.Y)
                        tl = new Vector2(tl.Value.X, v.Y);
                    if (v.X >= br.Value.X)
                        br = new Vector2(v.X, br.Value.Y);
                    if (v.Y >= br.Value.Y)
                        br = new Vector2(br.Value.X, v.Y);
                }
                return new Rectangle((int)tl.Value.X, (int)tl.Value.Y, (int)Math.Ceiling(br.Value.X - tl.Value.X), (int)Math.Ceiling(br.Value.Y - tl.Value.Y));
            }
        }

        public Vector2 ScreenToClient(Vector2 pos)
        {
            return Vector2.Transform(pos, Matrix.Invert(Matrix));
        }

        public abstract object Clone();
    }
}
